package com.hopes.bizz.common.core.util;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;

/**
 * @author: wyx
 * @since: 1.0.0
 * @version: [1.0.0, 2021/9/23]
 */
public class CoordinateSystemUtils {

    /**
     * 地球半径,单位 km
     */
    private static final double EARTH_RADIUS = 6378.137;

    /**
     * 根据经纬度，计算两点间的距离
     *
     * @param longitude1 第一个点的经度
     * @param latitude1  第一个点的纬度
     * @param longitude2 第二个点的经度
     * @param latitude2  第二个点的纬度
     * @return 返回距离 单位千米
     */
    public static double getDistance(double longitude1, double latitude1, double longitude2, double latitude2) {
        // 纬度
        double lat1 = Math.toRadians(latitude1);
        double lat2 = Math.toRadians(latitude2);
        // 经度
        double lng1 = Math.toRadians(longitude1);
        double lng2 = Math.toRadians(longitude2);
        // 纬度之差
        double a = lat1 - lat2;
        // 经度之差
        double b = lng1 - lng2;
        // 计算两点距离的公式
        double s = 2 * Math.asin(Math.sqrt(Math.pow(Math.sin(a / 2), 2) +
                Math.cos(lat1) * Math.cos(lat2) * Math.pow(Math.sin(b / 2), 2)));
        // 弧长乘地球半径, 返回单位: 米
        s =  s * EARTH_RADIUS * 1000;
        return s;
    }

    /**
     * 将JT808协议的坐标转为普通坐标系
     * @param longitude
     * @param latitude
     * @return
     */
    public static JSONObject transJT808ToNormalCoordinateSystem(String longitude, String latitude){
        JSONObject p = new JSONObject();

        StringBuilder sb1 = new StringBuilder();
        int start1 = longitude.length() - 6;
        sb1.append(longitude.substring(0, start1)).append(".").append(longitude.substring(start1, longitude.length()));
        p.put("lng", sb1.toString());

        StringBuilder sb2 = new StringBuilder();
        int start2 = latitude.length() - 6;
        sb2.append(latitude.substring(0, start2)).append(".").append(latitude.substring(start2, latitude.length()));
        p.put("lat", sb2.toString());

        return p;
    }

    public static boolean isPointInPolygon (double lng , double lat , JSONArray points) {
        int iSum, iCount, iIndex;
        double dLon1 = 0, dLon2 = 0, dLat1 = 0, dLat2 = 0, dLon;
        if (points.size() < 3) {
            return false;
        }
        iSum = 0;
        iCount = points.size();
        for (iIndex = 0; iIndex<iCount;iIndex++) {
            dLon1 = points.getJSONObject(iIndex).getDoubleValue("lng");
            dLat1 = points.getJSONObject(iIndex).getDoubleValue("lat");
            if (iIndex == iCount - 1) {
                dLon2 = points.getJSONObject(0).getDoubleValue("lng");
                dLat2 = points.getJSONObject(0).getDoubleValue("lat");
            } else {
                dLon2 = points.getJSONObject(iIndex + 1).getDoubleValue("lng");
                dLat2 = points.getJSONObject(iIndex + 1).getDoubleValue("lat");
            }
            // 以下语句判断A点是否在边的两端点的水平平行线之间，在则可能有交点，开始判断交点是否在左射线上
            if (((lat >= dLat1) && (lat < dLat2)) || ((lat >= dLat2) && (lat < dLat1))) {
                if (Math.abs(dLat1 - dLat2) > 0) {
                    //得到 A点向左射线与边的交点的x坐标：
                    dLon = dLon1 - ((dLon1 - dLon2) * (dLat1 - lat) ) / (dLat1 - dLat2);
                    // 如果交点在A点左侧（说明是做射线与 边的交点），则射线与边的全部交点数加一：
                    if (dLon < lng) {
                        iSum++;
                    }
                }
            }
        }
        if ((iSum % 2) != 0) {
            return true;
        }
        return false;
    }

    /**
     * 判断线是否在面内
     * @param linePoints  线的端点集合
     * @param points      面的端点集合
     * @return            是否在面内
     */
    public static boolean isLineInPolygon(JSONArray linePoints , JSONArray points) {
        // 丰富线上的点
        final JSONArray fullPoints = CoordinateSystemUtils.fullPointForLine(linePoints);
        final int size = fullPoints.size();
        for (int i = 0; i < size; i++) {
            final double lng = fullPoints.getJSONObject(i).getDoubleValue("lng");
            final double lat = fullPoints.getJSONObject(i).getDoubleValue("lat");
            if (CoordinateSystemUtils.isPointInPolygon(lng, lat, points)) {
                return true;
            }
        }
        return false;
    }

    /**
     * 丰富线上的点
     * @param linePoints  线
     * @return
     */
    public static JSONArray fullPointForLine(JSONArray linePoints) {
        if (linePoints.size() < 2) {
            return linePoints;
        }
        final JSONArray result = new JSONArray();
        for (int i = 0; i < linePoints.size()-1; i++) {
            final JSONObject spoint = linePoints.getJSONObject(i);
            final JSONObject epoint = linePoints.getJSONObject(i + 1);
            result.addAll(fullPoints(spoint, epoint, true));
        }
        return result;
    }

    /**
     * 在两个经纬度坐标点之间的补点
     * @param spoint  起点坐标点
     * @param epoint  终点坐标点
     * @param includeSE 包含起点和终点
     * @return  补点后的坐标数组
     */
    private static JSONArray fullPoints(JSONObject spoint, JSONObject epoint, Boolean includeSE) {
        final JSONArray result = new JSONArray();
        if (null == spoint || null == epoint || null == includeSE) {
            return result;
        }
        final double sLng = spoint.getDoubleValue("lng");
        final double sLat = spoint.getDoubleValue("lat");
        final double eLng = epoint.getDoubleValue("lng");
        final double eLat = epoint.getDoubleValue("lat");

        // 设置的补点总数
        // final int stepNum = (int) (Math.max(Math.abs(eLng - sLng), Math.abs(eLat - sLat)) / 0.00000000001);
        final int stepNum = 10;
        final double lngStepLen = (eLng - sLng) / stepNum;
        final double latStepLen = (eLat - sLat) / stepNum;
        for (int i = 0; i < stepNum; i++) {
            final double lng = sLng + lngStepLen * i;
            final double lat = sLat + latStepLen * i;
            result.add(new JSONObject() {{ put("lng", lng); put("lat", lat); put("alt", 0); }});
        }
        if (includeSE) {
            result.add(0, spoint);
            result.add(epoint);
        }
        return result;
    }
}
